{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "I always enjoy showing people how much easier Numba makes it to speed up their NumPy-based technical codes.   With Numba, you usually can just write the code with loops and then add a decorator to your function and get speed-ups equivalent to having written the code in another compiled language (like C or Fortran).  \n",
    "\n",
    "Tonight when I saw this question on Stack Exchange: http://scicomp.stackexchange.com/questions/5473/how-to-express-this-complicated-expression-using-numpy-slices it looked like a perfect opportunity to test Numba again.\n",
    "\n",
    "So, I copied the looped_ver code from Nat Wilson (modified it slightly to make x[0] = 0) and then decorated it to let Numba compile the code.  The result continues to impress me about the code that Mark Florisson, Jon Riehl, and Siu Kwan Lam have put together.  Here is the equation that is being solved:\n",
    "\n",
    "$$\\displaystyle x_i = \\sum_{j=0}^{i-1} k_{i-j} a_{i-j} a_{j}$$\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Populating the interactive namespace from numpy and matplotlib\n"
     ]
    }
   ],
   "source": [
    "%pylab inline"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "from numba import jit\n",
    "\n",
    "def looped_ver(k, a):\n",
    "    x = np.empty_like(a)\n",
    "    x[0] = 0.0\n",
    "    for i in range(1, x.size):\n",
    "        sm = 0.0\n",
    "        for j in range(0, i):\n",
    "            sm += k[i-j,j] * a[i-j] * a[j]\n",
    "        x[i] = sm\n",
    "    return x\n",
    "\n",
    "eager_jit_ver = jit('f8[:](f8[:,:],f8[:])')(looped_ver)\n",
    "lazy_jit_ver = jit(looped_ver)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "import time\n",
    "import numpy as np\n",
    "repeat = 3\n",
    "\n",
    "def getbest(func, *args):\n",
    "    import time\n",
    "    best = 1e12\n",
    "    for i in range(repeat):\n",
    "        start = time.time()\n",
    "        func(*args)\n",
    "        current = time.time() - start\n",
    "        if current < best:\n",
    "            best = current\n",
    "    return best\n",
    "    \n",
    "\n",
    "def timeit(N):\n",
    "    res = {'looped':[], 'lazy_jit':[], 'eager_jit':[]}\n",
    "    for n in N:\n",
    "        k = np.random.rand(n,n)\n",
    "        a = np.random.rand(n)\n",
    "        for version in ['looped', 'lazy_jit', 'eager_jit']:\n",
    "            func = eval('%s_ver' % version)\n",
    "            res[version].append(getbest(func, k, a))\n",
    "    return res"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "N = [100,200,500,1000,5000]\n",
    "res = timeit(N)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Text(0.5, 0, 'Size (N)')"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plot(N, log10(res['eager_jit']), N, log10(res['lazy_jit']), N, log10(res['looped']))\n",
    "legend(['eager_jit', 'lazy_jit', 'Python'], loc='upper left')\n",
    "ylabel(r'$\\log_{10}$(time) in seconds')\n",
    "xlabel('Size (N)')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[296.1081081081081,\n",
       " 318.0652173913044,\n",
       " 216.0547839506173,\n",
       " 127.56937546232696,\n",
       " 48.22590635734276]"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "[res['looped'][i]/res['lazy_jit'][i] for i in range(len(N))]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.44.0dev0+379.g20176caf8.dirty\n"
     ]
    }
   ],
   "source": [
    "import numba\n",
    "print(numba.__version__)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This was run on a Macbook Pro.   Running `sysctl -n machdep.cpu.brand_string` resulted in:\n",
    "\n",
    "   Intel(R) Core(TM) i7-4870HQ CPU @ 2.50GHz"
   ]
  }
 ],
 "metadata": {
  "anaconda-cloud": {},
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
